////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//   Software       :                                                         //
//   Layer          : SRC                                                     //
//   Package        :                                                         //
//   Component      :                                                         //
//                                                                            //
//   File           : xsendkey.cxx                                            //
//   Author         : RSO ( Rafael Sotomayor Brûlé )                          //
//   EMail          : rsotomayor@savtec.cl                                    //
//   Type           : C++ Source                                              //
//   Usage          :                                                         //
//   Purpose        : Implementación de ATD y Funciones globales              //
//                                                                            //
//                                                                            //
//   who when        what                                                     //
//   ~~~ ~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//   RSO 09-10-2010  Creation                                                 //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef WIN32
#  include "windows_x11.h"
#else
#  define NeedFunctionPrototypes 1
#  include <X11/Xlib.h>
#  include <X11/keysym.h>
#  include <X11/keysymdef.h>


#  if XlibSpecificationRelease != 6
#      error Requires X11R6
#  endif

#endif

static char    *progname_g       = NULL;
static char    *displayname_g    = NULL;
static Window   window_g       = 0;
static Display *display_g        = NULL;
static char     keyname_g[1024];
static int      shift_g          = 0;
static int      keysym_g         = 0;

int	MyErrorHandler(Display *my_display, XErrorEvent *event)
{
    fprintf(stderr, "%s: XSendEvent(0x%lx) failed.\n", progname_g, window_g);
    return 1;
}

void	SendEvent(XKeyEvent *event)
{
   XSync(display_g, False);
    XSetErrorHandler(MyErrorHandler);
    XSendEvent(display_g, window_g, True, KeyPressMask, (XEvent*)event);
    XSync(display_g, False);
    XSetErrorHandler(NULL);
}

void	SendKeyPressedEvent(KeySym keysym, unsigned int shift)
{
    XKeyEvent		event;

    // Meta not yet implemented (Alt used instead ;->)
    int meta_mask=0;

    event.display	= display_g;
    event.window	= window_g;
    event.root		= RootWindow(display_g, 0); // XXX nonzero screens?
    event.subwindow	= None;
    event.time		= CurrentTime;
    event.x		= 1;
    event.y		= 1;
    event.x_root	= 1;
    event.y_root	= 1;
    event.same_screen	= True;
    event.type		= KeyPress;
    event.state		= 0;

    //
    // press down shift keys one at a time...
    //

    if (shift & ShiftMask) {
	event.keycode = XKeysymToKeycode(display_g, XK_Shift_L);
	SendEvent(&event);
	event.state |= ShiftMask;
    }
    if (shift & ControlMask) {
	event.keycode = XKeysymToKeycode(display_g, XK_Control_L);
	SendEvent(&event);
	event.state |= ControlMask;
    }

    if (shift & Mod1Mask) {
        event.keycode = XKeysymToKeycode(display_g, XK_Alt_L);
        SendEvent(&event);
        event.state |= Mod1Mask;
    }
    if (shift & meta_mask) {
	event.keycode = XKeysymToKeycode(display_g, XK_Meta_L);
	SendEvent(&event);
	event.state |= meta_mask;
    }

    //
    //  Now with shift keys held down, send event for the key itself...
    //


    // fprintf(stderr, "sym: 0x%x, name: %s\n", keysym, keyname);
    if (keysym != NoSymbol) {
        event.keycode = XKeysymToKeycode(display_g, keysym);
        // fprintf(stderr, "code: 0x%x, %d\n", event.keycode, event.keycode );
        SendEvent(&event);

        event.type = KeyRelease;
        SendEvent(&event);
    }

    //
    // Now release all the shift keys...
    //

    if (shift & ShiftMask) {
	event.keycode = XKeysymToKeycode(display_g, XK_Shift_L);
	SendEvent(&event);
	event.state &= ~ShiftMask;
    }
    if (shift & ControlMask) {
        event.keycode = XKeysymToKeycode(display_g, XK_Control_L);
        SendEvent(&event);
        event.state &= ~ControlMask;
    }
    if (shift & Mod1Mask) {
        event.keycode = XKeysymToKeycode(display_g, XK_Alt_L);
        SendEvent(&event);
        event.state &= ~Mod1Mask;
    }
    if (shift & meta_mask) {
	event.keycode = XKeysymToKeycode(display_g, XK_Meta_L);
	SendEvent(&event);
	event.state &= ~meta_mask;
    }
}


int	SendKey(char *key_p)
{
    int ii, Junk;
    int c;
    char *temp;
    unsigned long count = 1;

    strncpy(keyname_g, key_p, 1023);
    keysym_g = 0;
    shift_g  = 0;
    // fprintf(stderr, "keyname: %s\n", keyname);
    temp = strtok((char *)keyname_g, "+");
    while (temp != NULL) {
        // fprintf(stderr, "temp: %s\n", temp);
        if (strcmp(temp, "Alt") == 0)
            c = XK_Alt_L;
        else if (strcmp(temp, "Control") == 0)
            c = XK_Control_L;
        else if (strcmp(temp, "Back") == 0)
            c = XK_BackSpace;            
        else if (strcmp(temp, "Shift") == 0)
            c = XK_Shift_L;
        else
            c = XStringToKeysym(temp);

        if (c == 0) {
            fprintf(stderr, "unknown: %s\n", temp);
//                    usage();
        }

        switch (c) {
            case XK_Shift_L:
            case XK_Shift_R:
                shift_g |= ShiftMask;
                break;
            case XK_Control_L:
            case XK_Control_R:
                shift_g |= ControlMask;
                break;
            case XK_Caps_Lock:
            case XK_Shift_Lock:
                shift_g |= LockMask;
                break;
            case XK_Meta_L:
            case XK_Meta_R:
            case XK_Alt_L:
            case XK_Alt_R:
                shift_g |= Mod1Mask;
                break;
            case XK_Super_L:
            case XK_Super_R:
            case XK_Hyper_L:
            case XK_Hyper_R:
                break;
            default:
                keysym_g = c;
        }
        //fprintf(stderr, "keysym: 0x%x, shift: %d\n", keysym, shift);
        temp = strtok(NULL, "+");
    }
    // keysym = strtoul(argv[ii], NULL, 0); 


    if (keyname_g[0] == 0) {
        fprintf(stderr, "%s: you must specify a keyname.\n", progname_g);
        exit(1);
    }

    if(displayname_g == NULL)
      displayname_g = getenv("DISPLAY");

    if(displayname_g == NULL)
      displayname_g = (char *) ":0.0";

    display_g = XOpenDisplay(displayname_g);

    if(display_g == NULL)
    {
      fprintf(stderr, "%s: can't open display `%s'.\n",
      progname_g, displayname_g);
      exit(1);
    }

//    if(window_g == 0) {
      XGetInputFocus(display_g, &window_g, &Junk);
//    }

    if(window_g == (Window)-1)
    {
        window_g = RootWindow(display_g, 0); // XXX nonzero screens?
    }

    // now do the work:
    for (ii=0;ii<(int)count;ii++)
      SendKeyPressedEvent(keysym_g, shift_g);

    XCloseDisplay(display_g);

    return 0;
}


int	SendKeySym(int keysym_p,int shift_p)
{
	int Junk;

    if(displayname_g == NULL)
      displayname_g = getenv("DISPLAY");

    if(displayname_g == NULL)
      displayname_g = (char *) ":0.0";

    display_g = XOpenDisplay(displayname_g);

    if(display_g == NULL)
    {
      fprintf(stderr, "%s: can't open display `%s'.\n",
      progname_g, displayname_g);
      exit(1);
    }

//    if(window_g == 0)
        XGetInputFocus(display_g, &window_g, &Junk);

    if(window_g == (Window)-1)
    {
        window_g = RootWindow(display_g, 0); // XXX nonzero screens?
    }

    // now do the work:
//    for (ii=0;ii<(int)count;ii++)
	shift_g = shift_p;
	keysym_g = keysym_p;
	SendKeyPressedEvent(0x00, shift_g);
	SendKeyPressedEvent(keysym_g, shift_g);

  XCloseDisplay(display_g);

  return 0;
}

void	usage()
{
    fprintf(stderr,
        "usage: %s [-window <id>|root]\n"
        "            [-count <count>]\n"
        "            [-display <name>] [Modifier+[Modifier+]]Key\n"
        "(looks up keysyms in <X11/keysymdef.h>)\n"
        "'Alt',   'Control',   'Shift' are replaced by\n"
        "'Alt_L', 'Control_L', 'Shift_L'...\n"
        "  example:\n"
        "     %s Control+Alt+Right\n", progname_g, progname_g);
    exit(1);
}

int	mymainkey(int argc, char **argv)
{

    int ii, Junk;
    int c;
    char *temp;
    unsigned long count = 1;
    progname_g = argv[0];

    for(ii=1; ii<argc; ii++) {
        if(strcmp(argv[ii], "-display") == 0) {
            if(++ii == argc)
                usage();

            displayname_g = argv[ii];
        }
        else if(strcmp(argv[ii], "-count") == 0) {
            if(++ii == argc)
                usage();

            count = strtoul(argv[ii],NULL,0);
        }
        else if(strcmp(argv[ii], "-window") == 0) {
            if(++ii == argc)
                usage();

            if(strcmp(argv[ii], "root") == 0)
                window_g = (Window)-1;
            else
                window_g = (Window)strtoul(argv[ii], NULL, 0);
        }
        else if(argv[ii][0] == '-') {
            fprintf(stderr, "%s: bad option `%s'.\n",
                progname_g, argv[ii]);
            usage();
        }
        else {
            strncpy(keyname_g, argv[ii], 1023);
            keysym_g = 0;
            shift_g  = 0;
            // fprintf(stderr, "keyname: %s\n", keyname);
            temp = strtok((char *)keyname_g, "+");
            while (temp != NULL) {
                // fprintf(stderr, "temp: %s\n", temp);
                if (strcmp(temp, "Alt") == 0)
                    c = XK_Alt_L;
                else if (strcmp(temp, "Control") == 0)
                    c = XK_Control_L;
                else if (strcmp(temp, "Shift") == 0)
                    c = XK_Shift_L;
                else
                    c = XStringToKeysym(temp);

                if (c == 0) {
                    fprintf(stderr, "unknown: %s\n", temp);
                    usage();
                }

                switch (c) {
                    case XK_Shift_L:
                    case XK_Shift_R:
                        shift_g |= ShiftMask;
                        break;
                    case XK_Control_L:
                    case XK_Control_R:
                        shift_g |= ControlMask;
                        break;
                    case XK_Caps_Lock:
                    case XK_Shift_Lock:
                        shift_g |= LockMask;
                        break;
                    case XK_Meta_L:
                    case XK_Meta_R:
                    case XK_Alt_L:
                    case XK_Alt_R:
                        shift_g |= Mod1Mask;
                        break;
                    case XK_Super_L:
                    case XK_Super_R:
                    case XK_Hyper_L:
                    case XK_Hyper_R:
                        break;
                    default:
                        keysym_g = c;
                }
                // fprintf(stderr, "keysym: 0x%x, shift: %d\n", keysym, shift);
                temp = strtok(NULL, "+");
            }
            // keysym = strtoul(argv[ii], NULL, 0); 
        }
    }
    if (keyname_g[0] == 0) {
        fprintf(stderr, "%s: you must specify a keyname.\n", progname_g);
        exit(1);
    }

    if(displayname_g == NULL) {
    	displayname_g = getenv("DISPLAY");
    }

    if(displayname_g == NULL) {
      displayname_g = (char *) ":0.0";
    }

    display_g = XOpenDisplay(displayname_g);

    if(display_g == NULL) {
      fprintf(stderr, "%s: can't open display `%s'.\n",
      progname_g, displayname_g);
      exit(1);
    }

//    if(window_g == 0)
        XGetInputFocus(display_g, &window_g, &Junk);

    if(window_g == (Window)-1)
    {
        window_g = RootWindow(display_g, 0); // XXX nonzero screens?
    }

    // now do the work:
    for (ii=0;ii<(int)count;ii++)
      SendKeyPressedEvent(keysym_g, shift_g);

    XCloseDisplay(display_g);

    return 0;
}

